﻿/*   
    Copyright (C) 2009 Galaktika Corporation ZAO

    This file is a part of Galaktika.BusinessMonitor
 
    Galaktika.BusinessMonitor is a free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.
      
    You should have received a copy of the GNU General Public License
    along with Galaktika.BusinessMonitor.  If not, see <http://www.gnu.org/licenses/>.
  
    If GPL v.3 is not suitable for your products or company,
    Galaktika Corp provides Galaktika.BusinessMonitor under a flexible commercial license
    designed to meet your specific usage and distribution requirements.
    If you have already obtained a commercial license from Galaktika Corp,
    you can use this file under those license terms.
*/

using System;
using System.Collections.Generic;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Shapes;
using System.Windows.Media;

namespace Galaktika.BI.Silverlight.ClientApp
{
    public class LayoutControl : UserControl
    {
        class GridProcessingStepItem
        {
            public GridProcessingStepItem(
                Grid parent, 
                RowDefinition row, 
                ColumnDefinition column,
                GridDescriptor descriptor)
            {
                this.Parent = parent;
                this.Column = column;
                this.Row = row;
                this.Descriptor = descriptor;
            }

            public readonly Grid Parent;
            public readonly GridDescriptor Descriptor;
            public ColumnDefinition Column;
            public RowDefinition Row;
        }

        class GridProcessingContext
        {
            public readonly Stack<GridProcessingStepItem> Parents = new Stack<GridProcessingStepItem>();
        }

        public LayoutControl()
            : this(false)
        {
        }

        public LayoutControl(bool isScalable)
        {
            this.IsScalable = isScalable;
        }

        private static DependencyProperty IsScalableProperty =
            DependencyProperty.Register("IsScalableProperty", typeof(bool), typeof(LayoutControl), null);
        public bool IsScalable 
        {
            get
            {
                return (bool)base.GetValue(IsScalableProperty);
            }
            set
            {
                base.SetValue(IsScalableProperty, value);
            }
        } 

        private Dictionary<string, LayoutControlItem> m_Items = new Dictionary<string, LayoutControlItem>();
        private AppearanceGroup m_Appearance;
        public void LoadLayout(string xml, bool trimBottom)
        {
            m_Items.Clear();
            LayoutContainer layout = LayoutParser.Parse(xml);
            m_Appearance = layout.Root.Appearance;
            GridDescriptor descriptor = GridDescriptor.Create(layout, trimBottom);
            Grid grid = new Grid();
            if (this.IsScalable)
            {
                base.Content = new ScrollViewer()
                {
                    VerticalScrollBarVisibility = ScrollBarVisibility.Auto,
                    HorizontalScrollBarVisibility = ScrollBarVisibility.Auto,
                    Content = grid,
                    Style = App.Current.Resources["ScrollViewerGlowStyle"] as Style
                };
            }
            else
            {
                base.Content = grid;
            }
            var context = new GridProcessingContext();
            context.Parents.Push(
                new GridProcessingStepItem(grid, null, null, descriptor));
            this.ProcessGridDescriptor(context);
        }

        public void AddElement(UIElement element)
        {
            FrameworkElement fe = element as FrameworkElement;
            if (fe == null) return;
            LayoutControlItem item;
            m_Items.TryGetValue(fe.Name, out item);
            if (item == null) return;

            item.AssignContent(element, m_Appearance);
        }

        private void ProcessGridDescriptor(
            GridProcessingContext context)
        {
            GridDescriptor descriptor = context.Parents.Peek().Descriptor;
            if (descriptor.IsGroup)
            {
                this.CreateLayoutGroupElement(context);
            }
            else
            {
                if (descriptor.IsLeaf)
                {
                    switch (descriptor.LayoutItem.TypeName)
                    {
                        case LayoutItemTypeName.EmptySpaceItem:
                            this.CreateEmptySpaceItemElement(context);
                            break;
                        case LayoutItemTypeName.LayoutControlItem:
                            this.CreateLeafElement(context);
                            break;
                        case LayoutItemTypeName.SimpleSeparator:
                            this.CreateSimpleSeparatorElement(context);
                            break;
                        case LayoutItemTypeName.SimpleLabelItem:
                            this.CreateSimpleLabelElement(context);
                            break;
                        //case LayoutItemTypeName.LayoutGroup:
                        //    this.CreateLayoutGroupElement(parent, parentRow, parentColumn, descriptor);
                        //    break;
                        default:
                            break;
                    }
                }
                else
                {
                    this.CreateLayoutItemElement(context);
                }
            }
        }

        private void CreateSimpleLabelElement(
            GridProcessingContext context)
        {
            var stepItem = context.Parents.Peek();
            TextBlock text = this.CreateHeader(stepItem.Descriptor.LayoutItem);
            stepItem.Parent.Children.Add(text);
        }

        private void CreateSimpleSeparatorElement(
            GridProcessingContext context)
        {
            Rectangle rect = new Rectangle();
            rect.Fill = new SolidColorBrush(Color.FromArgb(0xFF, 0x80, 0x80, 0x80));
            var stepItem = context.Parents.Peek();
            stepItem.Parent.Children.Add(rect);
        }

        private void CreateLeafElement(
            GridProcessingContext context)
        {
            var stepItem = context.Parents.Peek();
            if (stepItem.Row == null)
            {
                stepItem.Row = new RowDefinition();
                stepItem.Column = new ColumnDefinition();
                stepItem.Parent.RowDefinitions.Add(stepItem.Row);
                stepItem.Parent.ColumnDefinitions.Add(stepItem.Column);
            }

            LayoutItemBase lib = stepItem.Descriptor.LayoutItem;
            var col = stepItem.Column;
            var row = stepItem.Row;
            var parent = stepItem.Parent;
            // max size
            //
            if (lib.MaxSize.Width > 0.0)
            {
                //col.MaxWidth = lib.MaxSize.Width;
                this.SetColumnMaxWidth(context.Parents, lib.MaxSize.Width);
            }
            if (lib.MaxSize.Height > 0.0)
            {
                //row.MaxHeight = lib.MaxSize.Height;
                this.SetRowMaxHeight(context.Parents, lib.MaxSize.Height);
            }
            // min size
            //
            if (lib.MinSize.Width > 0.0)
            {
                //col.MinWidth = lib.MinSize.Width;
                this.SetColumnMinWidth(context.Parents, lib.MinSize.Width);
            }
            if (lib.MinSize.Height > 0.0)
            {
                //row.MinHeight = lib.MinSize.Height;
                this.SetRowMinHeight(context.Parents, lib.MinSize.Height);
            }

            LayoutControlItem li = new LayoutControlItem(lib,parent);
            li.Column = col;
            li.Row = row;
            parent.Children.Add(li);
            if (!string.IsNullOrEmpty(lib.ControlName))
            {
                m_Items.Add(lib.ControlName, li);
            }
        }

        private void SetColumnMaxWidth(Stack<GridProcessingStepItem> steps, double maxWidth)
        {
            var b = false;
            foreach (var step in steps)
            {
                if (b && step.Descriptor.ColumnCount > 1)
                {
                    break;
                }
                if (step.Column != null)
                {
                    if (double.IsInfinity(step.Column.MaxWidth))
                    {
                        step.Column.MaxWidth = maxWidth;
                    }
                    else
                    {
                        step.Column.MaxWidth = Math.Max(step.Column.MaxWidth, maxWidth);
                    }
                    //if (step.Column.MaxWidth < maxWidth)
                    //{
                    //    step.Column.MaxWidth = maxWidth;
                    //}
                }
                b = true;
            }
        }

        private void SetColumnMinWidth(Stack<GridProcessingStepItem> steps, double minWidth)
        {
            var b = false;
            foreach (var step in steps)
            {
                if (b && step.Descriptor.ColumnCount > 1)
                {
                    break;
                }
                if (step.Column != null)
                {
                    step.Column.MinWidth = Math.Max(step.Column.MinWidth, minWidth);
                    //if (step.Column.MinWidth > minWidth)
                    //{
                    //    step.Column.MinWidth = minWidth;
                    //}
                }
                b = true;
            }
        }

        private void SetRowMaxHeight(Stack<GridProcessingStepItem> steps, double maxHeight)
        {
            var b = false;
            foreach (var step in steps)
            {
                if (b && step.Descriptor.RowCount > 1)
                {
                    break;
                }
                if (step.Row != null)
                {
                    if (double.IsInfinity(step.Row.MaxHeight))
                    {
                        step.Row.MaxHeight = maxHeight;
                    }
                    else
                    {
                        step.Row.MaxHeight = Math.Max(step.Row.MaxHeight, maxHeight);
                    }
                    //if (step.Row.MaxHeight < maxHeight)
                    //{
                    //    step.Row.MaxHeight = maxHeight;
                    //}
                }
                b = true;
            }
        }

        private void SetRowMinHeight(Stack<GridProcessingStepItem> steps, double minHeight)
        {
            var b = false;
            foreach (var step in steps)
            {
                if (b && step.Descriptor.RowCount > 1)
                {
                    break;
                }
                if (step.Row != null)
                {
                    step.Row.MinHeight = Math.Max(step.Row.MinHeight, minHeight);
                }
                b = true;
            }
        }

        private void CreateEmptySpaceItemElement(
            GridProcessingContext context)
        {
            // TODO: обработать EmptySpaceItem
            this.CreateLeafElement(context);
        }

        private void CreateLayoutItemElement(
            GridProcessingContext context)
        {
            var stepItem = context.Parents.Peek();
            var descriptor = stepItem.Descriptor;
            if (descriptor.Children.Count > 0)
            {
                var col = stepItem.Column;
                var row = stepItem.Row;
                var parent = stepItem.Parent;

                for (int i = 0; i < descriptor.RowCount; i++)
                {
                    parent.RowDefinitions.Add(new RowDefinition());
                }
                for (int i = 0; i < descriptor.ColumnCount; i++)
                {
                    parent.ColumnDefinitions.Add(new ColumnDefinition());
                }

                double[] columnWeights = new double[descriptor.ColumnCount];
                double minColumnWeight = double.MaxValue;
                double[] rowWeights = new double[descriptor.RowCount];
                double minRowHeight = double.MaxValue;

                foreach (GridCellDescriptor child in descriptor.Children)
                {
                    Grid grid = new Grid();
                    Grid.SetColumn(grid, child.ColumnIndex);
                    Grid.SetRow(grid, child.RowIndex);
                    parent.Children.Add(grid);

                    columnWeights[child.ColumnIndex] = child.Bounds.Width;
                    rowWeights[child.RowIndex] = child.Bounds.Height;
                    minColumnWeight = Math.Min(minColumnWeight, child.Bounds.Width);
                    minRowHeight = Math.Min(minRowHeight, child.Bounds.Height);

                    context.Parents.Push(new GridProcessingStepItem(
                        grid,
                        parent.RowDefinitions[child.RowIndex],
                        parent.ColumnDefinitions[child.ColumnIndex],
                        child.Child));
                    this.ProcessGridDescriptor(
                        context);
                    context.Parents.Pop();
                }

                for (int i = 0; i < columnWeights.Length; i++)
                {
                    //if (parent.ColumnDefinitions[i].Width == GridLength.Auto) continue;
                    columnWeights[i] = columnWeights[i] / minColumnWeight;
                    parent.ColumnDefinitions[i].Width = new GridLength(columnWeights[i], GridUnitType.Star);
                }
                for (int i = 0; i < rowWeights.Length; i++)
                {
                    //if (parent.RowDefinitions[i].Height == GridLength.Auto) continue;
                    rowWeights[i] = rowWeights[i] / minRowHeight;
                    parent.RowDefinitions[i].Height = new GridLength(rowWeights[i], GridUnitType.Star);
                }
            }
        }

        private void CreateLayoutGroupElement(
            GridProcessingContext context)
        {
            RowDefinition headerRow = new RowDefinition() { Height = GridLength.Auto };
            RowDefinition contentRow = new RowDefinition();// { Height = GridLength.Auto };
            ColumnDefinition contentColumn = new ColumnDefinition();// { Width = GridLength.Auto };

            var stepItem = context.Parents.Peek();
            var parent = stepItem.Parent;
            var descriptor = stepItem.Descriptor;
            parent.RowDefinitions.Add(headerRow);
            parent.RowDefinitions.Add(contentRow);
            parent.ColumnDefinitions.Add(contentColumn);

            Grid content = new Grid();
            if (descriptor.LayoutItem.IsTextVisible)
            {
                TextBlock header = this.CreateHeader(descriptor.LayoutItem);

                //Border header = new Border() { Background = new SolidColorBrush(Colors.Red) };
                //header.Child = header1;
                Grid.SetRow(header, 0);
                Grid.SetColumn(header, 0);
                parent.Children.Add(header);
            }

            Grid.SetRow(content, 1);
            Grid.SetColumn(content, 0);
            parent.Children.Add(content);

            context.Parents.Push(new GridProcessingStepItem(
                content,
                contentRow,
                contentColumn,
                descriptor));
            this.CreateLayoutItemElement(
                context);
            context.Parents.Pop();
        }

        private TextBlock CreateHeader(LayoutItemBase li)
        {
            TextBlock header = new TextBlock();
            header.Text = li.Text;

            return header;
        }
    }
}